(function (vue, name) {
  vue.component(name, {
    template: '<div class="yu-workflow" style="border: 1px solid #dadce0">\
        <slot name="dialog"></slot>\
        <div :id="splashID" :style="splashStyle">\
          <img src="libs/mxgraph/images/loading.gif">\
        </div>\
        <el-row style="padding:0;background-color:rgba(234,234,234,1)">\
          <el-col :span="24" style="margin-bottom: 1px;"><div :id="toolbarID" class="workflow-toolbar"><slot name="toolbar"></slot></div></el-col>\
          <el-col :span="24">\
            <div :id="sidebarID" class="geSidebarContainer"><slot name="sidebar"></slot></div>\
            <div :id="containerID" class="yu-workflow-container" style="position:relative;background:url(\'libs/mxgraph/images/grid.gif\');" :style="{height:height +\'px\'}"></div>\
            <div :id="formatbarID" class="workflow-format geSidebarContainer geFormatContainer"><slot name="format"></slot></div>\
          </el-col>\
        </el-row>\
      </div>',
    props: {
      height: { // 画布高度
        type: Number,
        default: 500
      },
      toolbarClass: { // 工具条样式
        type: String,
        default: ''
      },
      sidebarItems: {
        type: Array,
        default: function () {
          return [];
        }
      },
      showSidebar: { // 工具条样式
        type: Boolean,
        default: true
      },
      showToolbar: { // 左侧工具调
        type: Boolean,
        default: true
      },
      lock: { // 是否锁定graph
        type: Boolean,
        default: false
      },
	  param: { // 评论信息
        type: Array,
        default: function () {
          return [];
        }
      }
    },
    computed: {
      splashStyle: function () {
        return {
          width: '100%',
          height: '100%',
          'line-height': this.height + 'px',
          background: 'white',
          position: 'absolute',
          top: '0px',
          left: '0px',
          'z-index': 11,
          'text-align': 'center',
          'vertical-align': 'middle'
        };
      }
    },
    data: function () {
      return {
        locked: this.viewType === 'view' || this.lock,
        editorui: null,
        graph: null,
        editor: null,
        outline: null,
        toolbar: null,
        layoutAnimate: true,
        layoutCount: 0,
        size: null,
        edges: [],
        containerID: '',
        splashID: '',
        toolbarID: '',
        outlineID: '',
        sidebarID: '',
        formatbarID: '',
        container: null,
        template: '',
        isLayouting: false, // 是否正在自动布局
        isIniting: true, // 是否正在初始化
        outlineHiddenClick: false
      };
    },
    created: function () {
      this.containerID = 'graph_' + new Date().getTime();
      this.splashID = 'splash_' + new Date().getTime();
      this.toolbarID = 'toolbar_' + new Date().getTime();
      this.outlineID = 'outline_' + new Date().getTime();
      this.sidebarID = 'sidebar_' + new Date().getTime();
      this.formatbarID = 'formatbar_' + new Date().getTime();
    },
    mounted: function () {
      this.$nextTick(function () {
        var _this = this;
        var hideSplash = function () {
          // Fades-out the splash screen
          var splash = document.getElementById(_this.splashID);

          if (splash != null) {
            try {
              window.mxEvent.release(splash);
              window.mxEffects.fadeOut(splash, 100, true);
            } catch (e) {
              splash.parentNode.removeChild(splash);
            }
          }
        };
        try {
          if (!window.mxClient.isBrowserSupported()) {
            window.mxUtils.error('Browser is not supported!', 200, false);
          } else {
            this.initEditor();
            hideSplash();
          }
        } catch (e) {
          hideSplash();
          window.mxUtils.alert('初始化mxGraph报错:' + e.message);
        }
      });
    },
    destroyed: function () {
      // this.clearGraph();
      // this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.destroy();
      // this.graph.getTooltipForCell = null;
      // this.graph.destroy();
      // this.graph = null;
      // this.editorui = null;
      this.$off();
    },
    methods: {
      initEditor: function () {
        window.actionDelage = function () {};
        var _this = this;
        var container = document.getElementById(this.containerID);
        window.mxResources.loadDefaultBundle = false;
        var bundle = window.mxResources.getDefaultBundle(window.RESOURCE_BASE, window.mxLanguage) ||
        window.mxResources.getSpecialBundle(window.RESOURCE_BASE, window.mxLanguage);

        mxPerimeter.CustomPerimeter = function (bounds, vertex, next, orthogonal) {
          var cx = bounds.getCenterX();
          var cy = bounds.getCenterY();
          var dx = next.x - cx;
          var dy = next.y - cy;
          var alpha = Math.atan2(dy, dx);
          var p = new mxPoint(0, 0);
          var pi = Math.PI;
          var pi2 = Math.PI / 2;
          var beta = pi2 - alpha;
          var t = Math.atan2(bounds.height, bounds.width);
          var margin = 2; // 锚点边距
          if (alpha < -pi + t || alpha > pi - t) {
            // Left edge 左侧锚点
            p.x = bounds.x - margin;
            p.y = dy === 0 ? cy - bounds.width * Math.tan(alpha) / 2 : cy;
          } else if (alpha < -t) {
            // Top Edge 顶部锚点
            p.y = bounds.y - margin;
            p.x = dx === 0 ? cx - bounds.height * Math.tan(beta) / 2 : cx;
          } else if (alpha < t) {
            // Right Edge 右侧锚点
            p.x = bounds.x + bounds.width + margin;
            p.y = dy === 0 ? cy + bounds.width * Math.tan(alpha) / 2 : cy;
          } else {
            // Bottom Edge 底部锚点
            p.y = bounds.y + bounds.height + margin;
            p.x = dx === 0 ? cx + bounds.height * Math.tan(beta) / 2 : cx;
          }
          return p;
        };
        mxStyleRegistry.putValue('customPerimeter', mxPerimeter.CustomPerimeter);
        mxLog.show = function () {};
        mxLog.debug = function (e) {
          console.log('mxgraph操作出错', e);
        };
        mxObjectCodec.prototype.getNodeCustomType = function (nodeType, node) {
          return nodeType;
        };
        mxCodec.prototype._documentAllNodes = [];
        mxCodec.prototype.getDocumentAllNodes = function (node) {
          if (node && node.childNodes && node.childNodes.length > 0) {
            for (var i = 0, l = node.childNodes.length; i < l; i++) {
              if (Object.prototype.toString.call(node.childNodes[i]) === '[object Element]') {
                this._documentAllNodes.push(node.childNodes[i]);
                this.getDocumentAllNodes(node.childNodes[i]);
              }
            }
          }
          return this._documentAllNodes;
        };
        mxCodec.prototype.getElementByTagName = function (tagName) {
          var tmpElement = [];
          var htmlAllCollection = this.document.all;
          // document.all在Chrome 65开始支持
          if (htmlAllCollection === undefined) {
            this._documentAllNodes = [];
            htmlAllCollection = this.getDocumentAllNodes(this.document);
          }
          for (var k in htmlAllCollection) {
            if (htmlAllCollection[k].tagName && htmlAllCollection[k].tagName.toLowerCase() === tagName.toLowerCase()) {
              tmpElement.push(htmlAllCollection[k]);
            }
          }
          return tmpElement;
        };
        mxCodec.prototype.getAttributesByTagName = function (tagName) {
          var tmpElements = this.getElementByTagName(tagName);
          if (tmpElements && tmpElements.length > 0) {
            var tmpAttributes = {};
            var tmpEle = tmpElements[0];
            for (var i = 0, l = tmpEle.attributes.length; i < l; i++) {
              tmpAttributes[tmpEle.attributes[i].name] = tmpEle.attributes[i].value;
            }
            return tmpAttributes;
          }
          return null;
        };
        mxEvent.disableContextMenu(container);
        // Fixes possible asynchronous requests
        window.mxUtils.getAll([bundle, window.STYLE_PATH + 'default.xml'], function (xhr) {
          // Adds bundle text to resources
          window.mxResources.parse(xhr[0].getText());
          var themes = {};
          themes[window.Graph.prototype.defaultThemeName] = xhr[1].getDocumentElement();
          _this.editorui = new window.EditorUi(new window.Editor(false, themes), container, undefined, {
            menuBar: false,
            toolbar: _this.toolbarID,
            sidebarFooter: false,
            formatbar: _this.formatbarID,
            sidebar: _this.showSidebar ? {
              items: _this.sidebarItems
            } : false
          });
          _this.graph = _this.editorui.editor.graph;
          _this.graph.setCellsResizable(false);
          _this.graph.centerZoom = true;
          _this.graph.keepEdgesInForeground = true;
          _this.graph.resetEdgesOnMove = true;
          _this.graph.cellsEditable = false; // 禁用编辑(连线的value)
          _this.editorui.sidebar.enableTooltips = false;
          _this.editorui.sidebar.isEnabled = false;
          // 连接事件
          _this.graph.addListener(mxEvent.CELL_CONNECTED, function (sender, evt) {
            var edge = evt.getProperty('edge');
            if (edge) {
              if (edge.target && edge.source) {
                edge.data = edge.data || {};
                edge.data.nid = edge.id;
                edge.data.source = edge.source.id;
                edge.data.target = edge.target.id;
              } else {
                if (!_this.isLayouting && !_this.isIniting) { // 初始化添加连线和自动布局的时候不判断连线是否有target或source
                  setTimeout(function () {
                    if (!edge.source || !edge.target) { // 需要延迟判断，刚添加连线时edge.target或edge.source为null
                      _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
                      _this.editorui.undo();
                      _this.editorui.editor.undoManager.history.pop();
                    }
                  }, 10);
                }
              }
            }
          });
          // 节点移动事件
          _this.graph.addListener(mxEvent.CELLS_MOVED, function (sender, evt) {
            var cell = evt.properties.cells[0];
            if (cell && cell.edge) {
              if (!cell.target || !cell.source) {
                _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
                setTimeout(function () {
                  _this.editorui.undo();
                  _this.editorui.editor.undoManager.history.pop(); // 删除历史记录中最后一条
                });
                return false;
              }
            } else if (cell && cell.vertex && cell.edges) { // 拖动了节点 则重置它的连线样式(重置连线连接的起始节点和目标节点的锚点)
              _this.graph.setCellStyles('exitX', null, cell.edges);
              _this.graph.setCellStyles('entryX', null, cell.edges);
              _this.graph.setCellStyles('exitY', null, cell.edges);
              _this.graph.setCellStyles('entryY', null, cell.edges);
            }
          });

          _this.$emit('init');
          // 图元新增事件
          _this.graph.addListener(mxEvent.CELLS_ADDED, function (sender, evt) {
            const cell = evt.properties.cells[0];
            // 如果是线，且没有结束或者开始节点，就直接删除
            if (cell && cell.edge) {
              if (!cell.target || !cell.source) {
                _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
                setTimeout(function () {
                  _this.removeUndoManagerHistory();
                  // 注意此处回滚操作
                  _this.editorui.undo();
                  // 删除历史记录中最后一条
                  _this.editorui.editor.undoManager.history.splice(_this.editorui.editor.undoManager.history.length - 1);
                  return false;
                });
              }
              if (cell.target && cell.source && (cell.target.id === cell.source.id)) {
                _this.$message({message: '连线开始和结束不能指向相同节点!', type: 'warning'});
                setTimeout(function () {
                  _this.removeUndoManagerHistory();
                  // 注意此处回滚操作
                  _this.editorui.undo();
                  // 删除历史记录中最后一条
                  _this.editorui.editor.undoManager.history.splice(_this.editorui.editor.undoManager.history.length - 1);
                });
                return false;
              }
              if (!_this.checkEdgeOfCells(cell)) {
                cell.data = cell.data || {};
                cell.data.nid = cell.id;
                cell.data.label = ''; // '连线' + cell.id;
                cell.data.target = cell.target.id;
                cell.data.source = cell.source.id;
                _this.$emit('addedge', cell);
              } else {
                _this.$message({message: '节点之间存在相同连线!', type: 'warning'});
                setTimeout(function () {
                  _this.removeUndoManagerHistory();
                  // 注意此处回滚操作
                  _this.editorui.undo();
                  // 删除历史记录中最后一条
                  _this.editorui.editor.undoManager.history.splice(_this.editorui.editor.undoManager.history.length - 1);
                });
                return false;
              }
            }
            for (var i in evt.getProperty('cells')) {
              const vertex = evt.properties.cells[i];
              if (vertex.vertex) {
                if (vertex.data && (vertex.data.nodeType == 'C' || vertex.data.nodeType == '4')) {
                  var tmpGeo = mxUtils.clone(vertex.getGeometry());
                  tmpGeo.width = 39;
                  tmpGeo.height = 39;
                  vertex.setGeometry(tmpGeo);
                } else if (vertex.data && vertex.data.nodeType == '0') {
                  var tmpGeo = mxUtils.clone(vertex.getGeometry());
                  tmpGeo.width = 35;
                  tmpGeo.height = 35;
                  vertex.setGeometry(tmpGeo);
                } else if (vertex.data && (vertex.data.nodeType == '2' || vertex.data.nodeType == '3' || vertex.data.nodeType == '5')) {
                  var tmpGeo = mxUtils.clone(vertex.getGeometry());
                  tmpGeo.width = 40;
                  tmpGeo.height = 40;
                  vertex.setGeometry(tmpGeo);
                } else if (vertex.data && vertex.data.nodeType == '1') {
                  var tmpGeo = mxUtils.clone(vertex.getGeometry());
                  tmpGeo.width = 44;
                  tmpGeo.height = 44;
                  vertex.setGeometry(tmpGeo);
                }
              }
            }
            if (cell.vertex) {
              // 更新节点样式
              _this.updateCellImage(evt.getProperty('cells')[0]);
            }
            if (cell) {
              // console.log(cell)
              // console.log(cell.data)
              _this.checkCellsId();
            }
            return false;
          });
          // 删除节点时，将相关联的连线也删除
          _this.graph.addListener(mxEvent.REMOVE_CELLS, function (sender, evt) {
            evt.properties.cells.forEach(function (cell) {
              // 删除节点时 遍历所有的连线，删除无起点或终点连线
              if (cell.vertex) {
                _this.deleteInvalidEdge();
              }
              // 节点删除后可能还存在于_this.graph.getModel().cells中, 需要手动删除一下
              var cells = _this.graph.getModel().cells;
              for (var k in cells) {
                if (cells[k].id === cell.id) {
                  delete _this.graph.getModel().cells[k];
                }
              }
            });
          });
          _this.graph.addListener(window.mxEvent.CLICK, function (sender, evt) {
            var cell = evt.getProperty('cell');
            _this.$emit('cellclick', cell);
          });
          _this.graph.view.addListener(mxEvent.SCALE, function graphScaleFns (sender, evt) {
            _this.$emit('scale', evt);
          });
          // 设置节点提示框 #TODO 节点提示框信息
          _this.graph.setTooltips(true);
          _this.graph.getTooltipForCell = function (cell) {
            if (cell.vertex) {
          	  if (_this.param) {
          	  	  for (var k in _this.param) {
          	  	    if (_this.param[k].nid == cell.data.nid) {
          	  		  return _this.param[k].comment + '&nbsp';
          	  		}
          	  	  }
          		  return '节点ID:' + cell.data.nid + '&nbsp';
          	  } else {
          	    return '节点ID:' + cell.data.nid + '&nbsp';
          	    }
          	} else {
          	  return '路由ID:' + cell.data.nid + '&nbsp';
          	}
          };
          _this.locked && _this.lockGraph();
          _this.layout();
        }, function () {
          container.innerHTML = '<center style="margin-top:10%;">Error loading resource files. Please check browser console.</center>';
        });
      },
      // 遍历所有的连线，删除无起点或终点连线
      deleteInvalidEdge: function () {
        var _this = this;
        var cells = _this.graph.getModel().cells;
        var hasNeedDelete = false;
        for (var c in cells) {
          if (cells[c].edge && !cells[c].vertex && (!cells[c].target || !cells[c].source)) {
            _this.graph.removeCells([cells[c]]);
            hasNeedDelete = true;
          }
        }
        if (hasNeedDelete) {
          _this.deleteInvalidEdge();
        }
      },
      setNodeType: function (type) {
        mxObjectCodec.prototype.nodeType = type;
        mxObjectCodec.prototype.getNodeCustomType = function (nodeType, node) {
          if (node.getAttribute('vertex') === '1') {
            return 'Node';
          }
          if (node.getAttribute('edge') === '1') {
            return 'Line';
          }
          return nodeType;
        };
      },
      doAction: function (fn) {
        this.editorui.actions.actions[fn].funct();
      },
      hideSidebar: function (h) {
        this.editorui.hsplitPosition = h ? 64 : 0;
        // 重新布局
        this.editorui.refresh();
      },
      hideFormatbar: function (h) {
        this.editorui.formatWidth = h ? 240 : 0;
        // 重新布局
        this.editorui.refresh();
      },
      // 更新节点信息
      updateCell: function (id, key, value, nodeType) {
        if (id) {
          var cell = this.getVertex(id, nodeType);
          if (cell) {
            switch (key) {
            case 'label':
              this.updateCellLabel(cell, value);
              break;
            case 'data':
              cell.data = yufp.clone(value, {});
              break;
            default:
              key && (cell[key] = value);
              break;
            }
          }
        }
      },
      // 更新节点图标
      updateCellImage: function (cell) {
        if (cell) {
          this.graph.setCellStyles('image', 'libs/mxgraph/images/' + cell.data.nodeType + '.png', [cell]);
          // 如果是开始节点和结束节点，更新图标大小
          this.updateCellLabel(cell, cell.data.label);
          // 节点绑定数据集
          cell.data = cell.data || {};
          cell.data.nid = cell.id;
        }
      },
      // 更新节点标记
      updateCellLabel: function (cell, label) {
        this.graph.getModel().setValue(cell, label);
        // 移除undo中信息
        this.removeUndoManagerHistory();
      },
      // 移除部分不需要的历史记录
      removeUndoManagerHistory: function () {
        var history = this.editorui.editor.undoManager.history;
        var item;
        for (var len = history.length, i = len - 1; i >= 0; i--) {
          item = history[i];
          if (item.changes && item.changes.length === 1 && item.changes[0] instanceof mxValueChange) {
            this.editorui.editor.undoManager.history.splice(this.editorui.editor.undoManager.history.length - 1);
            this.editorui.editor.undoManager.indexOfNextAdd = this.editorui.editor.undoManager.history.length;
          }
        }
        // console.log('操作历史信息', this.editorui.editor.undoManager.history);
      },
      graphScale: function (scale) {
        this.graph && this.graph.zoomTo(scale);
      },
      setSidebar: function (items) {
        this.editorui.sidebar.addItems(items);
      },
      addToolItems: function (items) {
        this.editorui.toolbar.addCustomItems(items);
      },
      // 锁定视图
      lockGraph: function () {
        var graph = this.graph;
        graph.getModel().beginUpdate();
        try {
          var cells = this.getGraphAllCells();
          graph.toggleCellStyles('locked', 0, cells);
          // graph.setEnabled(true);
        } finally {
          graph.getModel().endUpdate();
        }
      },
      setCellGeometry: function (vertex, size) {
        var tmpGeo = mxUtils.clone(vertex.getGeometry());
        tmpGeo.width = size;
        tmpGeo.height = size;
        vertex.setGeometry(tmpGeo);
        this.updateCellImage(vertex);
      },
      // 获取所有节点数组
      getGraphAllCells: function () {
        var cells = [], cs = this.graph.getModel().cells;
        for (var k in cs) {
          cells.push(cs[k]);
        }
        return cells;
      },
      // 获取所有节点类型数组
      getGraphAllvertex: function () {
        var cells = [], cs = this.graph.getModel().cells;
        for (var k in cs) {
          if (cs[k].vertex) {
            cells.push(cs[k]);
          }
        }
        return cells;
      },
      // 更改cell样式
      updateCellStyle: function (cell, type, value) {
        if (cell && type) {
          this.graph.setCellStyles(type, value, [cell]);
        }
      },
      // 校验流程图是否合法
      checkEdgeOfCells: function (cell) {
        var hasEdge = false;
        var cells = this.graph.getModel().cells;
        for (var k in cells) {
          if (cells[k].edge && cells[k].source && cells[k].target && cells[k].id !== cell.id) {
            if ((cells[k].source.id === cell.source.id && cells[k].target.id === cell.target.id) || (cells[k].source.id === cell.target.id && cells[k].target.id === cell.source.id)) {
              hasEdge = true;
              break;
            }
          }
        }
        return hasEdge;
      },
      // 校验流程图是否合法
      checkValidate: function () {
        var result = {
          valid: true,
          messages: []
        };
        var cells = this.graph.getModel().cells;
        var edges = [], vertexs = [], startCell = null, endCells = [];
        for (var k in cells) {
          if (cells[k].edge) {
            edges.push(cells[k]);
          } else if (cells[k].vertex) {
            if (cells[k].data.nodeType === 'S') {
              if (startCell) {
                result.messages.push('只能有一个开始节点');
                result.valid = false;
              }
              startCell = cells[k];
            } else if (cells[k].data.nodeType === 'E') {
              // 可以有多个结束节点
              /* if (endCell) {
                result.messages.push('只能有一个结束节点');
                result.valid = false;
              }*/
              endCells.push(cells[k]);
            } else {
              vertexs.push(cells[k]);
            }
          }
        }
        if (!startCell) {
          result.messages.push('必须有开始节点!');
          result.valid = false;
        }
        // 校验开始节点是否有入线
        if (startCell && startCell.edges && startCell.edges.length > 0) {
          startCell.edges.forEach(function (edge) {
            if (edge.source.id != startCell.id) {
              result.messages.push('开始节点不能有入线!');
              result.valid = false;
            }
          });
        }
        if (endCells.length < 1) {
          result.messages.push('必须有结束节点!');
          result.valid = false;
        }
        // 校验结束节点是否有出线
        for (var i = 0, length = endCells.length; i < length; i++) {
          if (endCells[i].edges && endCells[i].edges.length > 0) {
            endCells[i].edges.forEach(function (edge) {
              if (edge.target && edge.target.id != endCells[i].id) {
                result.messages.push('结束节点不能有出线!');
                result.valid = false;
              }
            });
          }
        }

        if (!vertexs || vertexs.length === 0) {
          result.messages.push('图中必须含有业务节点!');
          result.valid = false;
        }
        var hasC = false; // 是否有汇总节点
        var has2Or4 = false; // 是否有多选节点或条件多选节点
        // 校验孤立节点
        vertexs.forEach(function (v) {
          if (v.geometry) {
            if (!v.edges || v.edges.length < 2) {
              var str = '节点: ' + v.data.nid + '-' + v.data.label + ' 必须要有连线!';
              result.messages.push(str);
              result.valid = false;
            } else if (v.edges.length > 1) {
              // 校验任一非开始节点和结束节点，必须有进有出连线
              var hasIn = false, hasOut = false;
              for (var i = 0, l = v.edges.length, e; i < l; i++) {
                e = v.edges[i];
                if (e.target && e.source) {
                  if (e.target.id == v.id) {
                    hasOut = true;
                  }
                  if (e.source.id == v.id) {
                    hasIn = true;
                  }
                  // 判断连线两端不能同时为汇总节点
                  if ((v.id !== e.target.id && v.data.nodeType === 'C' && e.target.data.nodeType === 'C') || (v.id !== e.source.id && v.data.nodeType === 'C' && e.source.data.nodeType === 'C')) {
                    result.messages.push('汇总类型节点不能连接汇总类型节点!');
                    result.valid = false;
                    break;
                  }
                }
              }
              !hasIn && !hasOut && (result.valid = false);
            }
          }
          // 如果存在多选节点或条件多选节点，必须有汇总节点存在
          if (v.data.nodeType === '2' || v.data.nodeType === '4') {
            has2Or4 = true;
          }
          if (v.data.nodeType === 'C') {
            hasC = true;
          }
        });
        if (has2Or4 && !hasC) {
          result.messages.push('存在多选节点必须有对应的汇总节点!');
          result.valid = false;
        }
        // 如果没有连线
        if (!edges || edges.length === 0) {
          result.messages.push('连线缺失!');
          result.valid = false;
        } else {
          edges.forEach(function (edge) {
            if (edge.target && edge.source) {
              for (var i = 0, length = endCells.length; i < length; i++) {
                if (edge.target.id === endCells[i].id && edge.source.id === startCell.id) {
                  result.messages.push('开始节点不能直接连接结束节点!');
                  result.valid = false;
                }
              }
              // 校验连线target和source必须是节点
              if (edge.target.edge || edge.source.edge) {
                result.messages.push('连线必须连接节点!');
                result.valid = false;
              }
            } else {
              result.messages.push('连线必须连接两个节点!');
              result.valid = false;
            }
          });
        }
        // 多个结束节点时,结束节点可能缺少入线
        var flag = true;
        for (var i = 0, length = endCells.length; i < length; i++) {
          if (!this.hasIn4End(edges, endCells[i])) {
            flag = false;
            break;
          }
        }
        if (!flag) {
          result.messages.push('结束节点必须要有入线!');
          result.valid = false;
        }

        // 校验普通、单选、多选、条件单选、条件多选节点是否配置了处理人员
        vertexs.forEach(function (v) {
          if (v.data.nodeType === '2' || v.data.nodeType === '0' || v.data.nodeType === '1' || v.data.nodeType === '3' || v.data.nodeType === '4') {
            if (v.data.nodeUser === undefined || v.data.nodeUser === '' || v.data.nodeUser === null) {
              var str = '节点: ' + v.data.nid + '-' + v.data.label + ' 未配置处理人员!';
              result.messages.push(str);
              result.valid = false;
            }
          }
          if (v.data.nodeLevel) {
            var reg = /^[0-9]\d*$/;
            if (!reg.test(v.data.nodeLevel)) {
              var str = '节点: ' + v.data.nid + '-' + v.data.label + ' 的节点级别应为数字!';
              result.messages.push(str);
              result.valid = false;
            }
          }
        });
        return result;
      },
      hasIn4End: function (edges, endCell) {
        var flag = false;
        for (var i = 0, length = edges.length; i < length; i++) {
          // 线可能是独立的线
          if (edges[i].target && edges[i].target.id === endCell.id) {
            flag = true;
          }
        }
        return flag;
      },
      createOverlay: function (image, tooltip, align, verticalAlign, point, type) {
        var _this = this;
        var overlay = new mxCellOverlay(image, tooltip, align || mxConstants.ALIGN_RIGHT, verticalAlign || mxConstants.ALIGN_BOTTOM, point || new mxPoint(0, 0), 'pointer');
        overlay.addListener(mxEvent.CLICK, function (sender, evt) {
          _this.$emit('overlayclick', sender, evt.properties.cell);
        });
        overlay.type = type;
        return overlay;
      },
      layout: function () {
        if (!this.isLayouting) {
          var _this = this;
          this.isLayouting = true;
          var layout = new mxHierarchicalLayout(this.graph);
          layout.resizeParent = true;
          layout.moveParent = true;
          layout.execute(this.graph.getDefaultParent());
          this.graph.fit(); // 自适应
          this.graph.center(true, true, 0.5, 0.5); // 将画布放到容器中间
          var sc = this.graph.getView().getScale(); // 获取当前的缩放比例
          this.graph.zoomTo(Math.round(sc / 2)); // 在缩放一半，否则是满屏状态，不好看
          setTimeout(function () {
            _this.isLayouting = false;
          }, 500);
        }
      },
      // 清除节点运行图标
      clearCellOverLay: function () {
        var _this = this;
        var cells = this.graph.getModel().cells;
        var cell = null;
        for (var k in cells) {
          cell = cells[k];
          if (cell && cell.overlays) {
            var overlays = cell.overlays;
            for (var i = overlays.length - 1; i >= 0; i--) {
              _this.graph.removeCellOverlay(cell, overlays[i]);
            }
          }
        }
      },
      getVertex: function (id, nodeType) {
        var cells = this.graph.getModel().cells;
        for (var i in cells) {
          if (cells[i].id === id && cells[i][nodeType]) {
            return cells[i];
          }
        }
        // return this.graph.getModel().getCell(id);
      },
      // 加载xml文件,并返回workflow对象
      loadXML: function (xml) {
        var doc = mxUtils.parseXml(xml);
        this.graph.importGraphModel(doc.documentElement);
        // 加载完成后更新对应的数据信息
        var cells = this.graph.getModel().cells;
        var cell;
        for (var k in cells) {
          cell = cells[k];
          if (cell.vertex || cell.edge) {
            cell.id = cells[k].data.nid + '';
            // cells[k].data.nid = cell.id;
            this.traversalAttributes(cell.data, ['nid']);
          }
          // 更新开始节点结束节点图标大小, 临时调整 #TODO
          // if (cell.vertex && cell.data && (cell.data.nodeType === 'S' || cell.data.nodeType === 'E')) {
          //   var tmpGeo = mxUtils.clone(cell.getGeometry());
          //   tmpGeo.width = 42;
          //   tmpGeo.height = 42;
          //   cell.setGeometry(tmpGeo);
          // }
        }
        this.isIniting = false;
        var codec = new mxCodec(doc);
        return codec.getAttributesByTagName('workflow');
      },
      // 对于将数字转换成字符串
      traversalAttributes: function (o, exc) {
        for (var k in o) {
          if (this.isInteger(o[k]) && exc.indexOf(k) === -1) {
            o[k] += '';
          }
        }
      },
      isInteger: function (v) {
        return Object.prototype.toString.call(v) === '[object Number]';
      },
      // 对图节点排序
      orderGraph: function () {
        var cells = this.graph.getModel().cells;
        var tmpCells = [];
        for (var k in cells) {
          tmpCells.push(cells[k]);
        }
        this.graph.orderCells(false, tmpCells);
      },
      // 保存前处理数据,主要处理连线
      processCellData: function () {
        var cells = this.graph.getModel().cells;
        for (var k in cells) {
          if (cells[k].edge) {
            cells[k].data.target = cells[k].target.id;
            cells[k].data.source = cells[k].source.id;
          }
        }
      },
      // 查看xml内容
      getXML: function (data) {
        this.orderGraph();
        this.processCellData();
        var encoder = new mxCodec();
        var node = encoder.encode(this.graph.getModel());
        // 增加图信息节点
        var wf = encoder.document.createElement('Workflow');
        this.ergodicAttr(wf, data);
        node.firstChild.insertBefore(wf, node.firstChild.firstChild);
        return mxUtils.getPrettyXml(node);
      },
      // 导出xml文件
      exportXML: function (data) {
        var result = this.checkValidate();
        var status = result.valid;
        this.checkCellsId();
        // var message = this.splitMessages(result.messages);
        if (typeof status == 'boolean' && status === true) {
          this.orderGraph();
          this.processCellData();
          var encoder = new mxCodec();
          var node = encoder.encode(this.graph.getModel());
          // 增加图信息节点
          var wf = encoder.document.createElement('Workflow');
          this.ergodicAttr(wf, data);
          node.firstChild.insertBefore(wf, node.firstChild.firstChild);
          return mxUtils.getPrettyXml(node);
        } else {
          var h = this.$createElement;
          var messages = this.messageFormat(result.messages);
          this.$msgbox({
            message: h('div', null, messages),
            type: 'warning',
            lineBreak: true,
            showConfirmButton: true
          });
        }
      },
      checkCellsId: function () {
        var cells = this.graph.getModel().cells;
        var cellsId = [];
        for (var k in cells) {
          if (cellsId.indexOf(cells[k].id) > -1) {
            // cells[k].id = cells[k].id + '0000';
            if (cells[k].data) {
              cells[k].data.nid = cells[k].id;
            }
          }
          cellsId.push(cells[k].id);
        }
      },
      // 格式化
      messageFormat: function (messages) {
        var h = this.$createElement;
        for (var i = 0, len = messages.length; i < len; i++) {
          messages[i] = h('p', null, messages[i] + '\n');
        }
        return messages;
      },
      ergodicAttr: function (dom, data) {
        for (var k in data) {
          if (k !== 'flowContent' && k !== '$index') {
            dom.setAttribute(k, data[k] || '');
          }
        }
      },
      hiddenOutlineWindow: function (isReopen) {
        if (isReopen === false) {
          if (this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.window.isVisible() === true) {
            this.editorui.actions.outlineWindow.window.setVisible(isReopen);
            this.outlineHiddenClick = true;
          }
        } else if (this.outlineHiddenClick) {
          if (this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.window.isVisible() === false) {
            this.editorui.actions.get('outline').funct();
            this.outlineHiddenClick = false;
          }
        }
      },
      clearGraph: function () {
        this.graph && this.graph.removeCells(this.graph.getChildVertices(this.graph.getDefaultParent()));
      }
    }
  });
}(Vue, 'yu-workflow'));